package com.example.web;

import org.redisson.Redisson;
import org.redisson.RedissonReadLock;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author 86130-leon
 * @date 2024/1/11--17:01
 */
@RestController
public class IndexController {

    @Autowired
    private Redisson redisson;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 扣减库存 api
     * 主从同步时,master 加锁成功宕机,slave同步未成功且当选为新的master;可导致锁失效 -->新的master也可以加锁成功
     * @return
     */
    @RequestMapping("/deduct_stock")
    public String deductStock() {
        // synchronized (this){代码块} 无法处理集群并发问题
        String lockKey = "lockKey";
        /*Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "ZhuGe");// jedis.setnx(k,v) --> k不存在则设置k,v; k已存在,则不设置k,v
        stringRedisTemplate.expire(lockKey,10, TimeUnit.SECONDS);//设置k过期时间,防止应用宕机造成死锁*/

        /*String clientId = UUID.randomUUID().toString();//用来标记锁是不是自己创建的；
        Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,clientId,10,TimeUnit.SECONDS); //原子性的设置k,v 与k过期时间
        if (!result) {
            return "biz_code(没有抢到锁)";
        }*/

        RLock redissonLock = redisson.getLock(lockKey);// redisson实现分布式锁
        redissonLock.lock();
        try{
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));// jedis.get("stock")
            if (stock > 0) {
                int realStock = stock - 1;
                stringRedisTemplate.opsForValue().set("stock", realStock + " "); // jedis.set(k,v)
                System.out.println("扣减成功，剩余库存:" + realStock);
            } else {
                System.out.println("扣减失败，库存不足");
            }
        }finally {
            /*// 如果锁是自己添加的，则释放锁  -->防止多线程下,释放了别人的锁,导致超卖
            if(clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))){
                stringRedisTemplate.delete(lockKey); //删除锁
            }*/
            redissonLock.unlock();
        }
        return "end";
    }

    /**
     * redLock实现分布式锁; 避免了集群环境下主从同步, master加锁成功同步slave失败;导致的锁失效
     * 牺牲了性能; 因为超过半数的节点加锁成功-->才算加锁成功
     * @return
     */
    @RequestMapping("/redlock")
    public String redlock(){
        String lockKey="product_101";
        // 这里需要自己实例化不同的 redis实例的 redisson客户端连接，这里只是伪代码用一个 redisson 客户端简化了
        RLock lock1=redisson.getLock(lockKey);
        RLock lock2=redisson.getLock(lockKey);
        RLock lock3=redisson.getLock(lockKey);

        /**
         * 根据多个 RLock 对象构建 RedissonRedLock （最核心的差别就在这里）
         */
        RedissonRedLock redLock = new RedissonRedLock(lock1,lock2,lock3);
        try{
            /**
             * waitTimeout:尝试获取锁的最大等待时间，超过这个值，则认为获取锁失败
             * leaseTime: 锁的持有时间，超过这个时间锁会自动失效(值应设置为大于业务处理的时间，确保在锁有效期内业务能处理完)
             */
            boolean res = redLock.tryLock(10,30,TimeUnit.SECONDS);
            if(res){
                //成功获取锁，在这里处理业务
            }
        }catch (Exception e){
            throw new RuntimeException("lock fail");
        }finally {
            //无论如何，最后都要解锁
            redLock.unlock();
        }
        return "end";

    }


}
